// 认识所有权
// 所有权（系统）是 Rust 最为与众不同的特性，它让 Rust 无需垃圾回收器（garbage collector）即可保证内存安全

// 所有权规则
// 首先，让我们看一下所有权的规则。当我们通过举例说明时，请谨记这些规则：
//     Rust 中的每一个值都有一个被称为其 所有者（owner）的变量。
//     值在任一时刻有且只有一个所有者。
//     当所有者（变量）离开作用域，这个值将被丢弃。

fn ownership() {

    {                             // s 在这里无效, 它尚未声明
        let s = "hello";    // 从此处起，s 开始有效
        // 使用 s
    }                             // 此作用域已结束，s 不再有效

}


// 变量与数据交互的方式（一）：移动
// 总结： 对于基本数据类型，不存在移动效应而是直接拷贝，因为基本数据类型直接存储值本身。而对于引用类型，存在移动效应
fn ownership_1() {
    // 只在栈上的数据：拷贝
    let x = 5;
    // 将变量 x 的整数值赋给 y
    // 由于基本数据类型直接存储数据本身，属于值传递
    let y = x;
    println!("x is: {} and y is: {}", x, y);


    // s1 变量由三部分组成，一个指向存放字符串内容内存的指针，一个长度，和一个容量。这一组数据存储在栈上。
    // 右侧则是堆上存放内容的内存部分。
    // 长度表示 String 的内容当前使用了多少字节的内存。容量是 String 从分配器总共获取了多少字节的内存。
    // 长度与容量的区别是很重要的
    let s1 = String::from("hello");
    // 当我们将 s1 赋值给 s2，String 的数据被复制了，这意味着我们从栈上拷贝了它的指针、长度和容量。
    // 我们并没有复制指针指向的堆上数据。
    // 但是如果两个数据指针指向了同一位置。这就有了一个问题：当 s2 和 s1 离开作用域，
    // 他们都会尝试释放相同的内存。这是一个叫做 二次释放（double free）的错误，
    // 也是之前提到过的内存安全性 bug 之一。两次释放（相同）内存会导致内存污染，它可能会导致潜在的安全漏洞。
    // 为了确保内存安全，这种场景下 Rust 的处理有另一个细节值得注意。
    // 在 let s2 = s1 之后，Rust 认为 s1 不再有效，因此 Rust 不需要在 s1 离开作用域后清理任何东西。
    let s2 = s1;        // s1 被移动，从此失效，不可再用

}


// 变量与数据交互的方式（二）：克隆
fn ownership_2() {
    let s1 = String::from("hello");
    // 拷贝了 s1 堆上的数据并绑定到 s2
    let s2 = s1.clone();

    println!("s1 = {}, s2 = {}", s1, s2);
}


// 所有权与函数
// 将值传递给函数在语义上与给变量赋值相似。向函数传递值可能会移动或者复制，就像赋值语句一样
fn ownership_3() {
    let s = String::from("hello");  // s 进入作用域

    takes_ownership(s);             // s 的值移动到函数里 ...
    // ... 所以到这里 s 不再有效

    let x = 5;                      // x 进入作用域

    makes_copy(x);                  // x 应该移动函数里，
    // 但 i32 是 Copy 的，所以在后面可继续使用 x

} // 这里, x 先移出了作用域，然后是 s。但因为 s 的值已被移走，所以不会有特殊操作

fn takes_ownership(some_string: String) { // some_string 进入作用域
    println!("{}", some_string);
} // 这里，some_string 移出作用域并调用 `drop` 方法。占用的内存被释放

fn makes_copy(some_integer: i32) { // some_integer 进入作用域
    println!("{}", some_integer);
} // 这里，some_integer 移出作用域。不会有特殊操作



// 返回值与作用域
fn ownership_4() {
    let s1 = gives_ownership();         // gives_ownership 将返回值
    // 移给 s1

    let s2 = String::from("hello");     // s2 进入作用域

    let s3 = takes_and_gives_back(s2);  // s2 被移动到 takes_and_gives_back 函数中,
    // 它也将返回值移给 s3
} // 这里, s3 移出作用域并被丢弃。s2 也移出作用域，但已被移走，
// 所以什么也不会发生。s1 移出作用域并被丢弃

fn gives_ownership() -> String {           // gives_ownership 将返回值移动给
    // 调用它的函数

    let some_string = String::from("yours"); // some_string 进入作用域

    some_string                              // 返回 some_string 并移出给调用的函数
}

// takes_and_gives_back 将传入字符串并返回该值
fn takes_and_gives_back(a_string: String) -> String { // a_string 进入作用域

    a_string  // 返回 a_string 并移出给调用的函数
}


// 返回一组数据
fn ownership_5() {
    let s1 = String::from("hello");

    let (s2, len) = calculate_length(s1);

    println!("The length of '{}' is {}.", s2, len);
}

fn calculate_length(s: String) -> (String, usize) {
    let length = s.len(); // len() 返回字符串的长度

    (s, length)
}
